/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation 
 * 
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 * 
 * SPDX-License-Identifier: MIT
 **************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** FileX Component                                                       */
/**                                                                       */
/**   Media                                                               */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define FX_SOURCE_CODE


/* Include necessary system files.  */

#include "fx_api.h"
#include "fx_system.h"
#include "fx_directory.h"
#include "fx_media.h"
#include "fx_utility.h"


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _fx_media_volume_set                                PORTABLE C      */
/*                                                           6.1          */
/*  AUTHOR                                                                */
/*                                                                        */
/*    William E. Lamie, Microsoft Corporation                             */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function sets the volume name to the name supplied by the      */
/*    caller.                                                             */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    media_ptr                             Media control block pointer   */
/*    volume_name                           New volume name               */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    return status                                                       */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _fx_directory_entry_read              Read a directory entry        */
/*    _fx_utility_logical_sector_read       Read directory sector         */
/*    _fx_utility_logical_sector_write      Write directory sector        */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application Code                                                    */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
/*  09-30-2020     William E. Lamie         Modified comment(s),          */
/*                                            resulting in version 6.1    */
/*                                                                        */
/**************************************************************************/
UINT  _fx_media_volume_set(FX_MEDIA *media_ptr, CHAR *volume_name)
{

ULONG        i, j;
FX_DIR_ENTRY dir_entry, dir_entry1;
UINT         status, offset;
UCHAR       *work_ptr;
CHAR         alpha;


    /* Check the media to make sure it is open.  */
    if (media_ptr -> fx_media_id != FX_MEDIA_ID)
    {

        /* Return the media not opened error.  */
        return(FX_MEDIA_NOT_OPEN);
    }

    dir_entry.fx_dir_entry_log_sector = 0;
    dir_entry.fx_dir_entry_byte_offset = 0;

    /* If trace is enabled, insert this event into the trace buffer.  */
    FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_VOLUME_SET, media_ptr, volume_name, 0, 0, FX_TRACE_MEDIA_EVENTS, 0, 0)

    /* Protect against other threads accessing the media.  */
    FX_PROTECT


   /* First, check for an invalid volume name.  */
        if (volume_name[0] == 0)
        {

        /* Yes, volume name is invalid.  Return an error.  */
        return(FX_INVALID_NAME);
    }

    /* Read the logical directory sector 0 - we just do this to get a memory_buffer pointer */
    status =  _fx_utility_logical_sector_read(media_ptr, ((ULONG64) 0),
                                              media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR);

    /* Check the read status.  */
    if (status != FX_SUCCESS)
    {

        /* Release media protection.  */
        FX_UNPROTECT

        /* Return the error status.  */
        return(status);
    }

#ifndef FX_MEDIA_STATISTICS_DISABLE

    /* Increment the number of driver read boot sector requests.  */
    media_ptr -> fx_media_driver_boot_read_requests++;
#endif

    /* Build a driver request to read the boot record.  */
    media_ptr -> fx_media_driver_request =      FX_DRIVER_BOOT_READ;
    media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
    media_ptr -> fx_media_driver_buffer =       media_ptr -> fx_media_memory_buffer;
    media_ptr -> fx_media_driver_sectors =      1;
    media_ptr -> fx_media_driver_sector_type =  FX_BOOT_SECTOR;

    /* If trace is enabled, insert this event into the trace buffer.  */
    FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ, media_ptr, media_ptr -> fx_media_memory_buffer, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)

    /* Invoke the driver to read the boot sector.  */
        (media_ptr -> fx_media_driver_entry) (media_ptr);

    /* Determine if the request is successful.  */
    if (media_ptr -> fx_media_driver_status)
    {

        /* Release media protection.  */
        FX_UNPROTECT

        /* An error occurred in the driver.  */
        return(media_ptr -> fx_media_driver_status);
    }

    /* Calculate the offset based on the FAT present.  */
    if (media_ptr -> fx_media_32_bit_FAT)
    {

        /* FAT32 is present.  */
        offset =  FX_VOLUME_LABEL_32;
    }
    else
    {

        /* FAT12/16 is present.  */
        offset =  FX_VOLUME_LABEL;
    }

    /* Loop to store the volume name.  */
    for (i = 0; volume_name[i]; i++)
    {

        /* Have we reached the end?  */
        if (i == 11)
        {

            break;
        }

        /* Pickup volume name byte.  */
        alpha =  volume_name[i];

        /* Determine if alpha needs to be converted to upper case.  */
        if ((alpha >= 'a') && (alpha <= 'z'))
        {

            /* Convert alpha to upper case.  */
            alpha =  (CHAR)((INT)alpha - 0x20);
        }

        /* Store a byte of the volume name.  */
        media_ptr -> fx_media_memory_buffer[offset + i] =  (UCHAR)alpha;
    }

    /* Now pad with spaces.  */
    for (; i < 11; i++)
    {

        /* Append space character to volume name.  */
        media_ptr -> fx_media_memory_buffer[offset + i] =  0x20;
    }

#ifndef FX_MEDIA_STATISTICS_DISABLE

    /* Increment the number of driver write boot sector requests.  */
    media_ptr -> fx_media_driver_boot_write_requests++;
#endif

    /* Write the boot sector with the new volume name.  */
    media_ptr -> fx_media_driver_request =      FX_DRIVER_BOOT_WRITE;
    media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
    media_ptr -> fx_media_driver_buffer =       media_ptr -> fx_media_memory_buffer;
    media_ptr -> fx_media_driver_sectors =      1;
    media_ptr -> fx_media_driver_sector_type =  FX_BOOT_SECTOR;

    /* Set the system write flag since we are writing the boot sector.  */
    media_ptr -> fx_media_driver_system_write =  FX_TRUE;

    /* If trace is enabled, insert this event into the trace buffer.  */
    FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_WRITE, media_ptr, media_ptr -> fx_media_memory_buffer, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)

    /* Invoke the driver to write the boot sector.  */
        (media_ptr -> fx_media_driver_entry) (media_ptr);

    /* Clear the system write flag.  */
    media_ptr -> fx_media_driver_system_write =  FX_FALSE;

    /* Determine if the request is successful.  */
    if (media_ptr -> fx_media_driver_status)
    {

        /* Release media protection.  */
        FX_UNPROTECT

        /* An error occurred in the driver.  */
        return(media_ptr -> fx_media_driver_status);
    }

    /* Setup pointer to media name buffer.  */
    dir_entry1.fx_dir_entry_name =  media_ptr -> fx_media_name_buffer;

    /* Clear the short name string.  */
    dir_entry1.fx_dir_entry_short_name[0] =  0;

    /* Now we need to find the copy of the volume name in the root directory.  */
    i =  0;
    j =  media_ptr -> fx_media_root_directory_entries + 1;
    do
    {

        /* Read an entry from the root directory.  */
        status =  _fx_directory_entry_read(media_ptr, FX_NULL, &i, &dir_entry1);

        /* Check for error status.  */
        if (status != FX_SUCCESS)
        {

            /* Release media protection.  */
            FX_UNPROTECT

            /* Return to caller.  */
            return(status);
        }

        /* Determine if this is an empty entry.  */
        if ((dir_entry1.fx_dir_entry_name[0] == (CHAR)FX_DIR_ENTRY_FREE) && (dir_entry1.fx_dir_entry_short_name[0] == 0))
        {

            /* Yes, this is free entry.  Is it the first?  */
            if (i < j)
            {

                /* Yes, first free entry - remember it.  */
                dir_entry =  dir_entry1;
                j =  i;
            }
        }
        /* Determine if the directory entries are exhausted.  */
        else if (dir_entry1.fx_dir_entry_name[0] == FX_DIR_ENTRY_DONE)
        {

            /* Yes, this we are at the end of the directory.  Have there
            been any other free entries?  */
            if (i < j)
            {

                /* No, we need to remember this as the free entry.  */
                dir_entry =  dir_entry1;
                j =  i;
            }
            break;
        }
        /* Check for a volume name.  */
        else if (dir_entry1.fx_dir_entry_attributes & FX_VOLUME)
        {

            /* Yes, we have found a previously set volume name - use this entry.  */
            dir_entry =  dir_entry1;
            j =  i;
            break;
        }

        /* Move to next directory entry.  */
        i++;
    } while (i < media_ptr -> fx_media_root_directory_entries);

    /* Determine if a volume entry was not found and there are no more empty slots.  */
    if (i == media_ptr -> fx_media_root_directory_entries)
    {

        /* Determine if there was a free or previous volume name.  */
        if (j == (media_ptr -> fx_media_root_directory_entries + 1))
        {

            /* No, nothing was available in the root directory.  */

            /* Release media protection.  */
            FX_UNPROTECT

            /* No, existing volume name or space in the root directly was found, return an error.  */
            return(FX_MEDIA_INVALID);
        }
    }

    /* Now set the volume name and attribute.  */

    /* Read the logical directory sector.  */
    status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) dir_entry.fx_dir_entry_log_sector,
                                              media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_BOOT_SECTOR);

    /* Check the status of reading the directory entry. */
    if (status != FX_SUCCESS)
    {

        /* Release media protection.  */
        FX_UNPROTECT

        /* Return the error status.  */
        return(status);
    }

    /* Calculate pointer into directory buffer.  */
    work_ptr =  media_ptr -> fx_media_memory_buffer +
        (UINT)dir_entry.fx_dir_entry_byte_offset;

    /* Copy the volume name into the directory entry.  */
    for (i = 0; volume_name[i]; i++)
    {

        /* Have we reached the end?  */
        if (i == 11)
        {

            break;
        }

        /* Pickup volume name byte.  */
        alpha =  volume_name[i];

        /* Determine if alpha needs to be converted to upper case.  */
        if ((alpha >= 'a') && (alpha <= 'z'))
        {

            /* Convert alpha to upper case.  */
            alpha =  (CHAR)((INT)alpha - 0x20);
        }

        /* Store volume name.  */
        work_ptr[i] =  (UCHAR)alpha;
    }

    /* Pad with space characters.  */
    for (; i < 11; i++)
    {
        work_ptr[i] =  0x20;
    }

    /* Set the appropriate attributes.  */
    work_ptr[11] =  FX_VOLUME | FX_ARCHIVE;

    /* Set the other parts of the volume entry.  */

    /* Clear the hi word of cluster.  */
    work_ptr[20] =  0;
    work_ptr[21] =  0;

    /* Clear the low word of cluster.  */
    work_ptr[26] =  0;
    work_ptr[27] =  0;

    /* Clear the file size.  */
    work_ptr[28] =  0;
    work_ptr[29] =  0;
    work_ptr[30] =  0;
    work_ptr[31] =  0;

    /* Write the directory sector to the media.  */
    status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) dir_entry.fx_dir_entry_log_sector,
                                               media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);

    /* Release media protection.  */
    FX_UNPROTECT

    /* Return the status.  */
    return(status);
}

